﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Net;
using System.Net.NetworkInformation;
using System.ComponentModel;

namespace PingPong
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window, INotifyPropertyChanged
    {
        Pinger pinger = new Pinger();
        public Pinger Pinger
        {
            get { return pinger; }
            set { pinger = value; }
        }

        string searchString = "";
        public string SearchString
        {
            get { return searchString; }
            set { searchString = value; NotifyPropertyChanged("SearchString"); }
        }

        bool fetchHostInfo = false;
        public bool FetchHostInfo
        {
            get { return fetchHostInfo; }
            set { fetchHostInfo = value; NotifyPropertyChanged("FetchHostInfo"); }
        }

        public Window1()
        {
            InitializeComponent();

            SearchString = System.Configuration.ConfigurationSettings.AppSettings["DefaultSearchString"];

            int pingTimeout = pinger.PingTimeOut;
            bool valid = int.TryParse(System.Configuration.ConfigurationSettings.AppSettings["DefaultTimeout"], out pingTimeout);
            if (valid && pingTimeout > 0)
                pinger.PingTimeOut = pingTimeout;
            else
                MessageBox.Show(
                    "Ping timeout must be a positive integer. Using default timeout of " + pinger.PingTimeOut + " ms.",
                    "Invalid configuration settings",
                    MessageBoxButton.OK, MessageBoxImage.Warning);

            bool fetchHostInfo = false;
            if (bool.TryParse(System.Configuration.ConfigurationSettings.AppSettings["FetchHostInfo"], out fetchHostInfo))
                FetchHostInfo = fetchHostInfo;
            else
                MessageBox.Show(
                    "Fetch host info must be true/false. Disabling host info.",
                    "Invalid configuration settings",
                    MessageBoxButton.OK, MessageBoxImage.Warning);

            pinger.PingSent += new EventHandler<PingEventArgs>(pinger_PingSent);
            pinger.ReplyReceived += new EventHandler<PingEventArgs>(pinger_ReplyReceived);
            pinger.PingComplete += new EventHandler<PingCompleteEventArgs>(pinger_PingComplete);

            this.DataContext = this;
        }

        private void UpdateStatus(string text)
        {
            statusList.Items.Add(text);
            statusList.ScrollIntoView(statusList.Items[statusList.Items.Count - 1]);
        }


        private void PingSent(IPAddress address)
        {
            if (Dispatcher.CheckAccess())
                UpdateStatus("Pinging " + address.ToString() + " ... ");
            else
                Dispatcher.Invoke(DispatcherPriority.Normal, new Action<IPAddress>(PingSent), address);
        }

        private void ReplyReceived(PingCommand ping)
        {
            if (Dispatcher.CheckAccess())
            {
                if (ping.Reply.Status == System.Net.NetworkInformation.IPStatus.Success)
                {
                    string hostName = "";
                    if (FetchHostInfo)
                        hostName = pinger.GetHostInfo(ping.Address);

                    if (!string.IsNullOrEmpty(hostName))
                        replyList.Items.Add(ping.Address.ToString() + ": " + ping.Reply.RoundtripTime + "ms, Host: " + hostName);
                    else
                        replyList.Items.Add(ping.Address.ToString() + ": " + ping.Reply.RoundtripTime + "ms");

                    replyList.ScrollIntoView(replyList.Items[replyList.Items.Count - 1]);
                }
                statusList.Items[statusList.Items.Count - 1] += ping.Reply.Status.ToString();
                statusList.ScrollIntoView(statusList.Items[statusList.Items.Count - 1]);
            }
            else
                Dispatcher.Invoke(DispatcherPriority.Normal, new Action<PingCommand>(ReplyReceived), ping);
        }

        private void StartPing()
        {
            try
            {
                if (string.IsNullOrEmpty(pingTimeout.Text) || string.IsNullOrEmpty(SearchString))
                    return;

                IPRange[] ranges = Parser.ParseInput(SearchString);
                pinger.IPRanges = ranges;

                statusList.Items.Clear();
                replyList.Items.Clear();
                statusList.Items.Add("Started");

                pinger.Start();
            }
            catch (ArgumentException argEx)
            {
                MessageBox.Show(argEx.Message, "Parsing error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        void pinger_ReplyReceived(object sender, PingEventArgs e)
        {
            ReplyReceived(e.PingCommand);
        }

        void pinger_PingSent(object sender, PingEventArgs e)
        {
            PingSent(e.PingCommand.Address);
        }

        void pinger_PingComplete(object sender, PingCompleteEventArgs e)
        {
            DateTime dt = new DateTime(e.Elapsed.Ticks);
            string updateString = "";
            if (!e.IsCancelled)
                updateString = string.Format("Ping completed in {0}. Found {1} addresses", dt.ToString("mm:ss"), e.AddressesFound.Count);
            else
                updateString = string.Format("Ping cancelled after {0}. Found {1} addresses", dt.ToString("mm:ss"), e.AddressesFound.Count);

            if (Dispatcher.CheckAccess())
                UpdateStatus(updateString);
            else
                Dispatcher.Invoke(DispatcherPriority.Normal, new Action<string>(UpdateStatus), updateString);
        }

        private void startBtn_Click(object sender, RoutedEventArgs e)
        {
            if (!pinger.IsRunning)
                StartPing();
        }

        private void stopBtn_Click(object sender, RoutedEventArgs e)
        {
            pinger.Stop();
            statusList.Items.Add("Stopped");
        }

        private void exitBtn_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (pinger.IsRunning)
                pinger.Stop();
        }

        private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.DragMove();
        }

        #region INotifyPropertyChanged Members

        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
